/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {Scene} from 'arkanalyzer/lib/Scene';
import {SceneConfig} from 'arkanalyzer/lib/Config';
import {LOG_MODULE_TYPE} from 'arkanalyzer/lib/utils/logger';
import {buildFileDepGraph, buildModuleDepGraph} from './utils/depBuilder';
import {Logger} from 'arkanalyzer';
import {CommandLineArgs, parseCliArgs} from './utils/cliParser';
import {FileUtils, WriteFileMode} from '../utils/common/FileUtils';
import {HMS_SDK_SUB_PATH, initLogger, OHOS_SDK_SUB_PATH} from './utils/utils';
import path from 'path';
import fs from 'fs';

const logger = Logger.getLogger(LOG_MODULE_TYPE.TOOL, 'DependencyAnalysis');

// 构建场景
function buildScene(parsedArgs: CommandLineArgs): Scene {
    const projectDir = parsedArgs.projectPath;
    const sceneConfig = new SceneConfig();
    const ohosSdk = {
        name: 'ohosSdk',
        path: path.join(parsedArgs.sdkPath, OHOS_SDK_SUB_PATH),
        moduleName: ''
    };
    const hmsSdk = {
        name: 'hmsSdk',
        path: path.join(parsedArgs.sdkPath, HMS_SDK_SUB_PATH),
        moduleName: ''
    };
    sceneConfig.getSdksObj().push(...[ohosSdk, hmsSdk]);
    const projectName = path.basename(projectDir) ?? 'MyApplication';
    sceneConfig.buildFromProjectFiles(projectName, projectDir, parsedArgs.sourceFiles);

    // 构建场景
    const scene = new Scene();
    scene.buildSceneFromFiles(sceneConfig);
    logger.info(`Scene built.`);
    scene.inferTypes();
    logger.info('Type inference completed.');

    return scene;
}

function analyzeDependencies(scene: Scene, outputDir: string, mode: string): void {
    const outputSuffix = `${mode}DepGraph`;
    const depGraphJson = path.join(outputDir, `${outputSuffix}.json`);
    const depGraphDot = path.join(outputDir, `${outputSuffix}.dot`);

    logger.info(`Started to build ${mode} dependency graph...`);
    const depGraph = mode === 'file' ?
        buildFileDepGraph(scene.getFiles()) : buildModuleDepGraph(scene);

    const jsonRes = JSON.stringify(depGraph.toJson());
    FileUtils.writeToFile(depGraphJson, jsonRes, WriteFileMode.OVERWRITE);
    logger.info(`Building ${mode} dependency graph in json format has completed.`);

    const dotRes = depGraph.dump();
    FileUtils.writeToFile(depGraphDot, dotRes, WriteFileMode.OVERWRITE);
    logger.info(`Building ${mode} dependency graph in dot format has completed.`);

    logger.info(`Building ${mode} dependency graph completed.`);
}

function main(): void {
    initLogger();
    const parsedArgs = parseCliArgs();

    // create output directory
    if (!fs.existsSync(parsedArgs.outputDir)) {
        try {
            fs.mkdirSync(parsedArgs.outputDir, {recursive: true});
            logger.info(`Successfully created output directory ${parsedArgs.outputDir} .`);
        } catch (error) {
            logger.error(`Error creating output directory ${parsedArgs.outputDir}: ${error}`);
            process.exit(1);
        }
    }

    try {
        const scene = buildScene(parsedArgs);

        if (parsedArgs.mode === 'all') {
            analyzeDependencies(scene, parsedArgs.outputDir, 'file');
            analyzeDependencies(scene, parsedArgs.outputDir, 'module');
        } else {
            analyzeDependencies(scene, parsedArgs.outputDir, parsedArgs.mode);
        }
    } catch (error) {
        logger.error(`Error during execution: ${error instanceof Error ? error.message : String(error)}`);
        process.exit(1);
    }
}

// 执行主函数
main();